﻿using System;
using System.Collections.Generic;
using System.Drawing;

namespace NeuronDotNet
{
  /// <summary>
  /// Class River responsible for creating rivers and bridges on it and holds
  /// all info about them.
  /// Authors: Savenko Maria, Skorodumov Kirill.
  /// </summary>
  public class River
  {
    #region nested classes
    /// <summary>
    /// River can lie horisontal on the map - from left to right 
    /// borders of the map. And can be from top border of the map to bottonm 
    /// border.
    /// </summary>
    public enum RiverDirections
    {
      FromLeftToRight = 0,
      FromTopToBottom = 1
    }
    #endregion

    #region private fields
    /// <summary>
    /// Used for multiplying river Y coordinate, got by formula, on this number.
    /// </summary>
    private const int realRiverWidthFactor = 3;
    /// <summary>
    /// Multyplies real width on that number to prevent cities stay too close
    /// to the river and thus make graph look ugly after calculations.
    /// </summary>
    private const int fakeRiverWidthFactor = 4;
    #endregion

    #region public methods
    /// <summary>
    /// Creates new instance of River Class.
    /// </summary>
    public River()
    {
      RiverBank = new List<PointF>();
      Bridges = new List<PointF>();
    }

    #region getters/setters and public properties
    /// <summary>
    /// Gets width of the river for calculations.
    /// </summary>
    /// <returns>Width</returns>
    public double GetRiverWidth()
    {
      return realRiverWidthFactor * fakeRiverWidthFactor;
    }

    /// <summary>
    /// Gets the collection of points representing river.
    /// </summary>
    /// <returns>The set of river's points.</returns>
    public List<PointF> RiverBank { get; set; }

    /// <summary>
    /// Gets the collection of points representing bridges.
    /// </summary>
    /// <returns>The set of bridges' points.</returns>
    public List<PointF> Bridges { get; set; }

    /// <summary>
    /// River's direction on the map.
    /// </summary>
    public RiverDirections RiverDirection { get; set; }

    /// <summary>
    /// Offset from Map border.
    /// </summary>
    public int OffsetFromBorder { get; set; }
    #endregion

    /// <summary>
    /// Gets the closest bridge to a given point.
    /// </summary>
    /// <param name="point">The point to find the closest bridge for.</param>
    /// <returns>Found bridge coordinates as a point.</returns>
    public PointF GetClosestBridge(PointF point)
    {
      PointF foundBridge = Bridges[0];
      double distance = Utils.Distance(point, foundBridge);
      foreach (PointF bridge in Bridges)
      {
        double newDistance = Utils.Distance(point, bridge);
        if (newDistance < distance)
        {
          distance = newDistance;
          foundBridge = bridge;
        }
      }

      return foundBridge;
    }

    /// <summary>
    /// Gets value of river coordinate at given point
    /// </summary>
    /// <returns>Y coordinate.</returns>
    public float RiverCurve(double x)
    {
      return (float)(Math.Sin(x) * realRiverWidthFactor) + OffsetFromBorder;
    }

    /// <summary>
    /// Checks if two points are divided by current river.
    /// </summary>
    /// <param name="point1">First point.</param>
    /// <param name="point2">Second point.</param>
    /// <returns>True if two point are divided by river.</returns>
    public bool IfPointsDivided(PointF point1, PointF point2)
    {
      if (RiverDirection == RiverDirections.FromLeftToRight)
      {
        float y = RiverCurve(point1.X);
        float y2 = RiverCurve(point2.X);
        if (point1.Y >= y && point2.Y <= y2 || point2.Y >= y && point1.Y <= y2)
        {
          return true;
        }
      }
      else 
      {
        float y = RiverCurve(point1.Y);
        float y2 = RiverCurve(point2.Y);
        if (point1.X >= y && point2.X <= y2 || point2.X >= y && point1.X <= y2)
        {
          return true;
        }
      }
      return false;
    }
    #endregion
  }
}
